#pragma once

#include "Defines.h"
#include "Biome.h"
#include "Block Grid.h"
#include "Vector.h"

class World
{
private:
	Vector * AllGrids;
	Vector * GridsInUse;
	int ResidingGridID;
public:
//Tools to build the world

	//Built

	//Partially Built
	World(char * MapName);
	~World();
	bool Destroy();
	void CheckCurrentGrid(XY Pos);
	void GetCurrentGrid(XY Pos);

	//Will Need

	//May Need
	bool LinkWorld();
	void CheckAllData();
	void LevelEarth();
	void SmoothGround();
	void RaiseEarth();
	void LowerEarth();
	void AddWater();
	void MakeCave();
	void AddFoliage();
	void MakeTowers();
	void MakeSpawners();
};

World::World(char * MapName) {
	AllGrids = new Vector();
	GridsInUse = new Vector();
	XY GridPos;
	VectorData * pV = NULL;
	BlockGrid * pG = NULL;
	if( !CreateDirectoryA(MapName,NULL) ) {
		RemoveDirectoryA(MapName);
		if( !CreateDirectoryA(MapName,NULL) )
			return;
	}
	for(int y = 0;y < WORLD_SIZE;y++) {			
		for(int x = 0;x < WORLD_SIZE;x++) {
			pG = new BlockGrid();
			pG->Initialize(XY(x,y),MapName);
			pV = new VectorData(BLOCK_GRID,(void*)pG);
			AllGrids->Add( pV );
		}
	}
	LinkWorld();
}

World::~World() { Destroy(); }

bool World::Destroy() {
	if(AllGrids) {
		delete AllGrids;
	}
	if(GridsInUse) {
		delete GridsInUse;
	}
	if( !GridsInUse && !AllGrids) {
		return true;
	}
	else { return false; }
}

bool World::LinkWorld() {
	BlockGrid * pG = NULL, * pG2 = NULL;
	int AmountToLink = 0;
	int * GridsToLink = new int[WORLD_SIZE*WORLD_SIZE];
	for(int i = 0;i < AllGrids->GetSize();++i) {
		pG = VPBLOCK_GRID(AllGrids,i);
		if( !pG->Linked() ) {
			GridsToLink[AmountToLink] = i;
			AmountToLink++;
		}
	}
	for(int i = 0;i < AmountToLink;++i) {
		pG = VPBLOCK_GRID(AllGrids,GridsToLink[i]);
		for(int j = 0;j < AllGrids->GetSize();j++) {
			pG2 = VPBLOCK_GRID(AllGrids,j);
			if( pG->CheckNearby( pG2->GetGridPos() ) ) {
				pG->SetNearbyGrid( pG2 );
			}
		}
	}
	return true;
}

//Runs the first time to set everything up
// CheckCurrentGrid is optimized so it can run at each update phase
void World::GetCurrentGrid(XY Pos) {
	bool Found = false;
	int Loc = 0;

	//pV points to a storage container, pG points to an instance of
	//the objects that are being stored.
	//
	//I use the storage container pointer to interact with the 
	//vector
	// because it is much simpler and more uniform than sending the
	//data itself.
	VectorData * pV = NULL;
	BlockGrid * pG = NULL,* pG2 = NULL;

	//Am I in a grid? - Smallest O(1) - Largest O(n)
	for(int i = 0;i < AllGrids->GetSize();++i) {
		pV = AllGrids->Get(i); 
		if(pV->GetDataType() == BLOCK_GRID) {
			pG = VPBLOCK_GRID(AllGrids,i);
			if( pG->WithinBoundaries(Pos)) {
				Found = true;
				Loc = i;
				break;
			}
		}
	}
	//If not return
	if(!Found)ResidingGridID = -1;
	//Make sure the previous for loop succeeded
	else if( ResidingGridID >= 0 ) { 
		//Check all grids again 
		for(int i = 0;i < AllGrids->GetSize();++i) {
			pV = AllGrids->Get(i);
			pG2 = VPBLOCK_GRID(AllGrids,i);
			//If this grid is near the grid I am in, add
			// it to the list of active grids
			if( pG->CheckNearby( pG2->GetGridPos() ) ) {
				GridsInUse->Add( pV->GetDataType() , pV->GetData() );
			}
			//If it is not nearby but is still in the active
			// grid vector, remove it.
			else if( GridsInUse->Has( pG2 ) ) {
				GridsInUse->Remove_by_Value( pV->GetData() );
			}
		}
	}
}

void World::CheckCurrentGrid(XY Pos) {
	if(!VPBLOCK_GRID(AllGrids,ResidingGridID)->WithinBoundaries(Pos)) {
		GetCurrentGrid(Pos);
	}	
}